// 版权所有2021 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package noder

import (
	"bytes"
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/types2"
	"cmd/internal/src"
	"strings"
)

func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
	switch pkg {
	case nil:
		return types.BuiltinPkg
	case g.self:
		return types.LocalPkg
	case types2.Unsafe:
		return ir.Pkgs.Unsafe
	}
	return types.NewPkg(pkg.Path(), pkg.Name())
}

// typ将types2.Type转换为types.Type，包括缓存以前的
// 翻译类型。
func (g *irgen) typ(typ types2.Type) *types.Type {
	res := g.typ1(typ)

	// 计算前端看到的所有混凝土类型的尺寸。旧的
	// typechecker经常调用CheckSize（），我们希望消除调用
	// 最终，我们应该在这里做。我们只称之为
	// 顶级类型（即，我们在这里而不是在typ1中这样做），以确保
	// 在调用CheckSize之前，递归类型已经完全构造好了。
	if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() {
		types.CheckSize(res)
	}
	return res
}

// typ1与typ类似，但不调用CheckSize，因为它可能只有
// 递归类型的构造部分。不应该从外部调用
// 文件（g.typ是“外部”入口点）。
func (g *irgen) typ1(typ types2.Type) *types.Type {
	// 缓存类型2到类型映射。重要的是使每个定义的通用
	// 类型（实例化或未实例化）具有单个类型。类型表示。
	// 通过避免重新转换，还节省了大量计算和内存
	// 类型2重复输入。
	res, ok := g.typs[typ]
	if !ok {
		res = g.typ0(typ)
		g.typs[typ] = res
	}
	return res
}

// instTypeName2基于类型args为实例化类型创建名称
// （以类型2的形式给出）。
func instTypeName2(name string, targs []types2.Type) string {
	b := bytes.NewBufferString(name)
	b.WriteByte('[')
	for i, targ := range targs {
		if i > 0 {
			b.WriteByte(',')
		}
		tname := types2.TypeString(targ,
			func(*types2.Package) string { return "" })
		if strings.Index(tname, ", ") >= 0 {
			// types2.TypeString在类型中的逗号后放置空格
			// 列表，但实际类型名中不需要空格
			// 以及由此派生的方法/函数名。
			tname = strings.Replace(tname, ", ", ",", -1)
		}
		b.WriteString(tname)
	}
	b.WriteByte(']')
	return b.String()
}

// typ0将types2.Type转换为types.Type，但不执行缓存检查
// 在最高层。
func (g *irgen) typ0(typ types2.Type) *types.Type {
	switch typ := typ.(type) {
	case *types2.Basic:
		return g.basic(typ)
	case *types2.Named:
		if typ.TParams() != nil {
			// typ是已定义（命名）泛型类型的实例化。
			// 此实例化也应该是已定义（命名）的类型。
			// types2为我们提供了t.underground（）中的替换类型
			// 被替换的类型可能有，也可能没有
			// params。例如，我们可能正在替换一种类型
			// 另一类型param的param。

			if typ.TArgs() == nil {
				base.Fatalf("In typ0, Targs should be set if TParams is set")
			}

			// 转换为types.Type时，typ必须有名称，
			// 基于类型参数的名称。我们需要一个
			// 处理递归泛型类型的名称（以及
			// 打印字体时看起来更好）。
			instName := instTypeName2(typ.Obj().Name(), typ.TArgs())
			s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
			if s.Def != nil {
				// 我们已经遇到了这个实例，
				// 所以使用我们之前创建的类型，因为
				// 必须正好是定义类型的一个实例。
				return s.Def.Type()
			}

			// 首先创建一个转发类型并将其放入g.typs中
			// 映射，以便处理递归泛型类型。
			// 完全设置额外的NTIP信息（Def、RParams、，
			// 这可能会在翻译前设置HasTParam）
			// 底层类型本身，所以我们处理递归
			// 正确，包括通过方法签名。
			ntyp := newIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
			g.typs[typ] = ntyp

			// 如果NTIP仍然有类型参数，那么我们必须
			// 引用类似于“value[T2]”的内容，如
			// 指定方法的通用接收器，
			// 其中，值定义为“类型值[T any]
			// …”。保存类型args，它现在将是
			// 当前类型的新类型。
			// None
			// 如果NTIP没有类型参数，我们将保存
			// 用于实例化此类型的具体类型。我们将使用
			// 在实例化
			// 实例化类型。
			rparams := make([]*types.Type, len(typ.TArgs()))
			for i, targ := range typ.TArgs() {
				rparams[i] = g.typ1(targ)
			}
			ntyp.SetRParams(rparams)
			// fmt.Printf（“看到新类型%v%v\n”，instName，ntyp.HasTParam（））

			ntyp.SetUnderlying(g.typ1(typ.Underlying()))
			g.fillinMethods(typ, ntyp)
			return ntyp
		}
		obj := g.obj(typ.Obj())
		if obj.Op() != ir.OTYPE {
			base.FatalfAt(obj.Pos(), "expected type: %L", obj)
		}
		return obj.Type()

	case *types2.Array:
		return types.NewArray(g.typ1(typ.Elem()), typ.Len())
	case *types2.Chan:
		return types.NewChan(g.typ1(typ.Elem()), dirs[typ.Dir()])
	case *types2.Map:
		return types.NewMap(g.typ1(typ.Key()), g.typ1(typ.Elem()))
	case *types2.Pointer:
		return types.NewPtr(g.typ1(typ.Elem()))
	case *types2.Signature:
		return g.signature(nil, typ)
	case *types2.Slice:
		return types.NewSlice(g.typ1(typ.Elem()))

	case *types2.Struct:
		fields := make([]*types.Field, typ.NumFields())
		for i := range fields {
			v := typ.Field(i)
			f := types.NewField(g.pos(v), g.selector(v), g.typ1(v.Type()))
			f.Note = typ.Tag(i)
			if v.Embedded() {
				f.Embedded = 1
			}
			fields[i] = f
		}
		return types.NewStruct(g.tpkg(typ), fields)

	case *types2.Interface:
		embeddeds := make([]*types.Field, typ.NumEmbeddeds())
		j := 0
		for i := range embeddeds {
			// TODO（mdempsky）：获取嵌入位置。
			e := typ.EmbeddedType(i)
			if t := types2.AsInterface(e); t != nil && t.IsComparable() {
				// 忽略预定义类型“comparable”，因为它
				// 没有解决，也没有任何问题
				// 相关方法。
				continue
			}
			embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
			j++
		}
		embeddeds = embeddeds[:j]

		methods := make([]*types.Field, typ.NumExplicitMethods())
		for i := range methods {
			m := typ.ExplicitMethod(i)
			mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature))
			methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
		}

		return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...))

	case *types2.TypeParam:
		tp := types.NewTypeParam(g.tpkg(typ))
		// 将类型参数的名称保存在类型的sym中。
		// 在sym名称中包含types2下标
		sym := g.pkg(typ.Obj().Pkg()).Lookup(types2.TypeString(typ, func(*types2.Package) string { return "" }))
		tp.SetSym(sym)
		// 如果绑定方法引用typ，则设置g.typs[typ]。
		g.typs[typ] = tp

		// TODO（danscales）：我们目前不需要使用边界
		// 任何地方，所以最终我们可能会移除。
		bound := g.typ1(typ.Bound())
		*tp.Methods() = *bound.Methods()
		return tp

	case *types2.Tuple:
		// 元组用于函数调用的类型（即
		// 函数的返回值）。
		if typ == nil {
			return (*types.Type)(nil)
		}
		fields := make([]*types.Field, typ.Len())
		for i := range fields {
			fields[i] = g.param(typ.At(i))
		}
		t := types.NewStruct(types.LocalPkg, fields)
		t.StructType().Funarg = types.FunargResults
		return t

	default:
		base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ)
		panic("unreachable")
	}
}

// fillinMethods为定义的类型填充方法名称节点和类型。这
// 用于以后查找实例化类型的方法时的类型检查，
// 以及实际生成实例化类型的方法。
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
	if typ.NumMethods() != 0 {
		targs := make([]ir.Node, len(typ.TArgs()))
		for i, targ := range typ.TArgs() {
			targs[i] = ir.TypeNode(g.typ1(targ))
		}

		methods := make([]*types.Field, typ.NumMethods())
		for i := range methods {
			m := typ.Method(i)
			meth := g.obj(m)
			recvType := types2.AsSignature(m.Type()).Recv().Type()
			ptr := types2.AsPointer(recvType)
			if ptr != nil {
				recvType = ptr.Elem()
			}
			if recvType != types2.Type(typ) {
				// 不幸的是，甲基安非他明是该方法的一种
				// 泛型类型，所以我们必须进行替换才能得到
				// 实例化类型的方法的名称/类型，
				// 使用m.Type（）.RParams（）和typ.TArgs（）
				inst2 := instTypeName2("", typ.TArgs())
				name := meth.Sym().Name
				i1 := strings.Index(name, "[")
				i2 := strings.Index(name[i1:], "]")
				assert(i1 >= 0 && i2 >= 0)
				// 生成实例化方法的名称。
				name = name[0:i1] + inst2 + name[i1+i2+1:]
				newsym := meth.Sym().Pkg.Lookup(name)
				var meth2 *ir.Name
				if newsym.Def != nil {
					meth2 = newsym.Def.(*ir.Name)
				} else {
					meth2 = ir.NewNameAt(meth.Pos(), newsym)
					rparams := types2.AsSignature(m.Type()).RParams()
					tparams := make([]*types.Field, len(rparams))
					for i, rparam := range rparams {
						tparams[i] = types.NewField(src.NoXPos, nil, g.typ1(rparam.Type()))
					}
					assert(len(tparams) == len(targs))
					subst := &subster{
						g:       g,
						tparams: tparams,
						targs:   targs,
					}
					// 进行类型的替换
					meth2.SetType(subst.typ(meth.Type()))
					newsym.Def = meth2
				}
				meth = meth2
			}
			methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
			methods[i].Nname = meth
		}
		ntyp.Methods().Set(methods)
		if !ntyp.HasTParam() {
			// 为新的完全实例化类型生成所有方法。
			g.instTypeList = append(g.instTypeList, ntyp)
		}
	}
}

func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
	tparams2 := sig.TParams()
	tparams := make([]*types.Field, len(tparams2))
	for i := range tparams {
		tp := tparams2[i]
		tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type()))
	}

	do := func(typ *types2.Tuple) []*types.Field {
		fields := make([]*types.Field, typ.Len())
		for i := range fields {
			fields[i] = g.param(typ.At(i))
		}
		return fields
	}
	params := do(sig.Params())
	results := do(sig.Results())
	if sig.Variadic() {
		params[len(params)-1].SetIsDDD(true)
	}

	return types.NewSignature(g.tpkg(sig), recv, tparams, params, results)
}

func (g *irgen) param(v *types2.Var) *types.Field {
	return types.NewField(g.pos(v), g.sym(v), g.typ1(v.Type()))
}

func (g *irgen) sym(obj types2.Object) *types.Sym {
	if name := obj.Name(); name != "" {
		return g.pkg(obj.Pkg()).Lookup(obj.Name())
	}
	return nil
}

func (g *irgen) selector(obj types2.Object) *types.Sym {
	pkg, name := g.pkg(obj.Pkg()), obj.Name()
	if types.IsExported(name) {
		pkg = types.LocalPkg
	}
	return pkg.Lookup(name)
}

// tpkg返回包含函数、接口或结构类型的包
// 他的表情出现在他的脸上。
// None
// 警告：对于退化类型“func（）”、“interface{}”和
// “struct{}”，tpkg总是返回LocalPkg。然而，我们只需要
// 打包信息，以便go/types可以通过其API进行报告，以及
// 我们未能退回这些产品的原始包装的原因
// 特定类型是因为go/types不*报告
// 他们因此，在实践中，这种限制可能是没有意义的。
func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
	anyObj := func() types2.Object {
		switch typ := typ.(type) {
		case *types2.Signature:
			if recv := typ.Recv(); recv != nil {
				return recv
			}
			if params := typ.Params(); params.Len() > 0 {
				return params.At(0)
			}
			if results := typ.Results(); results.Len() > 0 {
				return results.At(0)
			}
		case *types2.Struct:
			if typ.NumFields() > 0 {
				return typ.Field(0)
			}
		case *types2.Interface:
			if typ.NumExplicitMethods() > 0 {
				return typ.ExplicitMethod(0)
			}
		}
		return nil
	}

	if obj := anyObj(); obj != nil {
		return g.pkg(obj.Pkg())
	}
	return types.LocalPkg
}

func (g *irgen) basic(typ *types2.Basic) *types.Type {
	switch typ.Name() {
	case "byte":
		return types.ByteType
	case "rune":
		return types.RuneType
	}
	return *basics[typ.Kind()]
}

var basics = [...]**types.Type{
	types2.Invalid:        new(*types.Type),
	types2.Bool:           &types.Types[types.TBOOL],
	types2.Int:            &types.Types[types.TINT],
	types2.Int8:           &types.Types[types.TINT8],
	types2.Int16:          &types.Types[types.TINT16],
	types2.Int32:          &types.Types[types.TINT32],
	types2.Int64:          &types.Types[types.TINT64],
	types2.Uint:           &types.Types[types.TUINT],
	types2.Uint8:          &types.Types[types.TUINT8],
	types2.Uint16:         &types.Types[types.TUINT16],
	types2.Uint32:         &types.Types[types.TUINT32],
	types2.Uint64:         &types.Types[types.TUINT64],
	types2.Uintptr:        &types.Types[types.TUINTPTR],
	types2.Float32:        &types.Types[types.TFLOAT32],
	types2.Float64:        &types.Types[types.TFLOAT64],
	types2.Complex64:      &types.Types[types.TCOMPLEX64],
	types2.Complex128:     &types.Types[types.TCOMPLEX128],
	types2.String:         &types.Types[types.TSTRING],
	types2.UnsafePointer:  &types.Types[types.TUNSAFEPTR],
	types2.UntypedBool:    &types.UntypedBool,
	types2.UntypedInt:     &types.UntypedInt,
	types2.UntypedRune:    &types.UntypedRune,
	types2.UntypedFloat:   &types.UntypedFloat,
	types2.UntypedComplex: &types.UntypedComplex,
	types2.UntypedString:  &types.UntypedString,
	types2.UntypedNil:     &types.Types[types.TNIL],
}

var dirs = [...]types.ChanDir{
	types2.SendRecv: types.Cboth,
	types2.SendOnly: types.Csend,
	types2.RecvOnly: types.Crecv,
}
